﻿
Imports System.IO
Imports std
Imports storoomc
Imports storcore

'a helper class for sliceType selection, datapath / reverseDiskSize configuration, etc
Public Class diskslices
    Private reserveDiskSize As UInt64
    Private slicecount As Int32
    Private datapath As String
    Private driveInfo As DriveInfo = Nothing
    Private sliceType As sliceTypeEnum
    Private slicesTypeFilter As String = Nothing
    Private _filters As map(Of String, String) = Nothing

    Private Enum sliceTypeEnum
        slice
        slice2
        slice3
        slice4
        slice5
    End Enum

    Public Sub New(ByVal slicesTypeFilter As String)
        copy(Me.slicesTypeFilter, slicesTypeFilter)
        assert(initial())
    End Sub

    Private Function filters() As map(Of String, String)
        If _filters Is Nothing Then
            _filters = New map(Of String, String)()
            _filters(configNaming.filter.slicestype) = slicesTypeFilter
        End If
        Return _filters
    End Function

    Private Function slicemgr() As storoomOper
        Return convertor.cast(Of storoomOper)(stornode.slicemgr)
    End Function

    Private Function initial() As Boolean
        reserveDiskSize = config.parameter(configNaming.stornode.name, _
                                           configNaming.stornode.reserveDiskSize, _
                                           configNaming.stornode.defaultValue.reserveDiskSize, _
                                           filters())
        slicecount = config.parameter(configNaming.stornode.name, _
                                      configNaming.stornode.slicecount, _
                                      configNaming.stornode.defaultValue.slicecount, _
                                      filters())
        slicemgr().enablecache = config.parameter(configNaming.stornode.name, _
                                                  configNaming.stornode.enablecache, _
                                                  configNaming.stornode.defaultValue.enablecache, _
                                                  filters())
        If istype(Of storoomDistrTM)(slicemgr()) Then
            With convertor.cast(Of storoomDistrTM)(slicemgr())
                .distributeOperationHoldTime = _
                    config.parameter(configNaming.stornode.name, _
                                     configNaming.stornode.distributeOperationHoldtime, _
                                     configNaming.stornode.defaultValue.distributeOperationHoldtime, _
                                     filters())
                .threadCount = config.parameter(configNaming.stornode.name, _
                                                configNaming.stornode.threadcount, _
                                                configNaming.stornode.defaultValue.threadcount, _
                                                filters())
            End With
        End If
        'it must be consistent with the one in slicesbase
        copy(datapath, stornode.datapath())
        driveInfo = New DriveInfo(Path.GetPathRoot(Path.GetFullPath(datapath)))
        assert(Not driveInfo Is Nothing, "cannot get driveInfo for datapath " + datapath)

        Dim sliceTypeStr As String = Nothing
        sliceTypeStr = config.parameter(configNaming.stornode.name, configNaming.stornode.sliceType, _
                                        configNaming.stornode.defaultValue.sliceType)
        If strsame(sliceTypeStr, "slice", False) Then
            sliceType = sliceTypeEnum.slice
        ElseIf strsame(sliceTypeStr, "slice2", False) Then
            sliceType = sliceTypeEnum.slice2
        ElseIf strsame(sliceTypeStr, "slice3", False) Then
            sliceType = sliceTypeEnum.slice3
        ElseIf strsame(sliceTypeStr, "slice5", False) Then
            sliceType = sliceTypeEnum.slice5
        Else
            sliceType = sliceTypeEnum.slice4
        End If

        If Not Directory.Exists(datapath) Then
            Try
                Directory.CreateDirectory(datapath)
            Catch ex As Exception
                raiseError(errorHandle.errorType.exclamation,
                           "cannot create directory " + datapath + ", slices will shutdown. ex ", _
                           ex.Message)
                Return False
            End Try
        End If

        Return True
    End Function

    Public Function maxSliceCount() As Int32
        Return slicecount
    End Function

    Public Function sliceModifiable() As Boolean
        Return sliceType <> sliceTypeEnum.slice
    End Function

    Public Function capacity(ByRef cap As UInt64) As Boolean
        If driveInfo Is Nothing Then
            raiseError("cannot get drive root from datapath " + datapath + ", check datapath setting." _
                        , errorHandle.errorType.exclamation)
            Return False
        Else
            Dim rtn As Int64
            rtn = driveInfo.AvailableFreeSpace - reserveDiskSize
            If rtn > 0 Then
                cap = rtn
            Else
                cap = 0
            End If
            Return True
        End If
    End Function

    Private Delegate Function sliceTypeDelegate(Of T As slicebase, RT)(ByVal i As T) As RT

    Private Shared Function cast(Of T As slicebase)(ByVal s As storageInterface) As T
        If s Is Nothing Then
            Return Nothing
        Else
            Return convertor.cast(Of T)(s)
        End If
    End Function

    Private Function sliceTypeSelector(Of RT)(ByVal slice As storageInterface, _
                                              ByVal s1 As sliceTypeDelegate(Of slice, RT), _
                                              ByVal s2 As sliceTypeDelegate(Of slice2, RT), _
                                              ByVal s3 As sliceTypeDelegate(Of slice3, RT), _
                                              ByVal s4 As sliceTypeDelegate(Of slice4, RT), _
                                              ByVal s5 As sliceTypeDelegate(Of slice5, RT)) As RT
        assert(Not s1 Is Nothing)
        assert(Not s2 Is Nothing)
        assert(Not s3 Is Nothing)
        assert(Not s4 Is Nothing)
        assert(Not s5 Is Nothing)
        If sliceType = sliceTypeEnum.slice Then
            Return s1(cast(Of slice)(slice))
        ElseIf sliceType = sliceTypeEnum.slice2 Then
            Return s2(cast(Of slice2)(slice))
        ElseIf sliceType = sliceTypeEnum.slice3 Then
            Return s3(cast(Of slice3)(slice))
        ElseIf sliceType = sliceTypeEnum.slice5 Then
            Return s5(cast(Of slice5)(slice))
        Else
            Return s4(cast(Of slice4)(slice))
        End If
    End Function

    Private Function generateSlice() As storageInterface
        Return sliceTypeSelector(Of storageInterface)(Nothing, _
                                                      Function(s As slice) As storageInterface
                                                          Return New slice()
                                                      End Function, _
                                                      Function(s As slice2) As storageInterface
                                                          Return New slice2()
                                                      End Function, _
                                                      Function(s As slice3) As storageInterface
                                                          Return New slice3()
                                                      End Function, _
                                                      Function(s As slice4) As storageInterface
                                                          Return New slice4()
                                                      End Function, _
                                                      Function(s As slice5) As storageInterface
                                                          Return New slice5()
                                                      End Function)
    End Function

    Public Function create(ByRef slice As storageInterface) As Boolean
        assert(slice Is Nothing)
        slice = generateSlice()
        Return convertor.cast(Of slicebase)(slice).create()
    End Function

    Public Function create(ByVal slice As storageInterface, ByVal filename As String) As Boolean
        Return convertor.cast(Of slicebase)(slice).create(filename)
    End Function

    Public Shared Function datafile(ByVal datapath As String, ByVal filename As String) As String
        Return combinepath(datapath, filename)
    End Function

    Public Function datafile(ByVal filename As String) As String
        Return datafile(datapath, filename)
    End Function

    Public Function existDatafile(ByVal filename As String) As Boolean
        Return File.Exists(datafile(filename))
    End Function

    Private Function open(ByVal slice As slicebase, ByVal filename As String) As Boolean
        Return slice.open(filename)
    End Function

    Public Function open(ByVal slice As storageInterface, ByVal filename As String) As Boolean
        Return open(convertor.cast(Of slicebase)(slice), filename)
    End Function

    Public Function listfiles(ByRef files() As String) As Boolean
        files = Directory.GetFiles(datapath)
        If files Is Nothing Then
            raiseError("cannot list file from datapath " + datapath, errorHandle.errorType.exclamation)
            Return False
        Else
            Return True
        End If
    End Function

    Public Function capacity_full() As Boolean
        Dim cap As UInt64
        Return ((Not capacity(cap)) OrElse (cap <= 0))
    End Function

    Public Function full(ByVal slices As vector(Of storageInterface)) As ternary
        If capacity_full() Then
            Return ternary.true
        Else
            'do not iterate each slice, if slices are created so often when write,
            'there are high possibility that most of the slices are full
            If slicecount <= slices.size() Then
                Return ternary.true
            Else
                Return ternary.false
            End If
        End If
    End Function

    Public Function open(ByVal slices As vector(Of storageInterface),
                         ByVal files() As String,
                         ByVal enableCreate As Boolean) As Boolean
        Dim i As Int32
        For i = 0 To files.Length() - 1
            If slices(i) Is Nothing Then
                If slices.size() <= i Then
                    slices.push_back(generateSlice())
                Else
                    slices(i) = generateSlice()
                End If
                Dim rtn As Boolean = False
                If existDatafile(files(i)) Then
                    rtn = open(slices(i), files(i))
                ElseIf enableCreate Then
                    rtn = create(slices(i), files(i))
                End If
                If Not rtn Then
                    Return False
                End If
            End If
        Next

        Return True
    End Function
End Class
